#undef BLENDSTRETCH
#undef BLENDPIXELSTRETCH
#undef BLENDBORDERSTRETCH

#define BLENDSTRETCH MAKE_BLEND(BLEND_MODE, PIXEL_BITS, StretchBilinear, )
#define BLENDPIXELSTRETCH MAKE_BLENDPIXELSTRETCH(BLEND_MODE, PIXEL_BITS, Bilinear, )
#define BLENDBORDERSTRETCH MAKE_BLENDBORDERSTRETCH(BLEND_MODE, PIXEL_BITS, Bilinear, )

#ifndef BLENDALPHA
__forceinline void BLENDPIXELSTRETCH(const PBITMAPEX lpbBitmap, LPBYTE c0, LPBYTE c1, DWORD u, DWORD v, LPXARGB result)
#else
__forceinline void BLENDPIXELSTRETCH(const PBITMAPEX lpbBitmap, LPBYTE c0, LPBYTE c1, DWORD u, DWORD v, LPXARGB result, DWORD alpha)
#endif
{
    DWORD m3 = (u * v) >> 8;
    DWORD m2 = u - m3;
    DWORD m1 = v - m3;
    DWORD m0 = 256 - m1- m2 - m3;

    DWORD c = GETPIXEL(lpbBitmap, c0).argb;
    DWORD BR = (c & 0x00FF00FF) * m0;
    DWORD GA = ((c & 0xFF00FF00) >> 8) * m0;
    c = GETPIXEL(lpbBitmap, c0 + PIXEL_BYTES).argb;;
    GA += ((c & 0xFF00FF00) >> 8) * m2;
    BR += (c & 0x00FF00FF)* m2;
    c = GETPIXEL(lpbBitmap, c1).argb;
    GA += ((c & 0xFF00FF00) >> 8) * m1;
    BR += (c & 0x00FF00FF) * m1;
    c = GETPIXEL(lpbBitmap, c1 + PIXEL_BYTES).argb;
    GA += ((c & 0xFF00FF00) >> 8) * m3;
    BR += (c & 0x00FF00FF) * m3;

    xARGB clr;
    clr.argb = (GA & 0xFF00FF00) | ((BR & 0xFF00FF00) >> 8);

#ifdef BLENDALPHA
    MulAlpha(clr, alpha);
#endif
    BLENDPIXEL(result, clr);
}

#ifndef BLENDALPHA
__forceinline void BLENDBORDERSTRETCH(const PBITMAPEX lpbBitmap, const int x16, const int y16, LPXARGB result)
#else
__forceinline void BLENDBORDERSTRETCH(const PBITMAPEX lpbBitmap, const int x16, const int y16, LPXARGB result, DWORD alpha)
#endif
{
    int x = (x16 >> 16);
    int y = (y16 >> 16);
    DWORD u16 = ((WORD)(x16));
    DWORD v16 = ((WORD)(y16));

    PIXEL pixel[4];
    int x0 = min(max(x, 0), lpbBitmap->bmWidth - 1);
    int x1 = min(max(x + 1, 0), lpbBitmap->bmWidth - 1);
    int y0 = min(max(y, 0), lpbBitmap->bmHeight - 1);
    int y1 = min(max(y + 1, 0), lpbBitmap->bmHeight - 1);
    pixel[0] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y0) + x0);
    pixel[1] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y0) + x1);
    pixel[2] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y1) + x0);
    pixel[3] = *((PIXEL*)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y1) + x1);

#ifndef BLENDALPHA
    BLENDPIXELSTRETCH(lpbBitmap, (PBYTE)&pixel[0], (PBYTE)&pixel[2], u16 >> 8, v16 >> 8, result);
#else
    BLENDPIXELSTRETCH(lpbBitmap, (PBYTE)&pixel[0], (PBYTE)&pixel[2], u16 >> 8, v16 >> 8, result, alpha);
#endif
}

#ifndef BLENDALPHA
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc)
#else
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, BYTE alpha)
#endif
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight + 1;

    const int csX = -(1 << 15) + ((int)xrIntFloat16 >> 1);
    const int csY = -(1 << 15) + ((int)yrIntFloat16 >> 1);

    int dstWidth = lpbDst->bmWidth;

    int borderY1 = -csY / (int)yrIntFloat16 + 1;
    if (borderY1 >= lpbDst->bmHeight)
        borderY1 = lpbDst->bmHeight;
    int borderX1 = -csX / (int)xrIntFloat16 + 1;
    if (borderX1 >= lpbDst->bmWidth)
        borderX1 = lpbDst->bmWidth;
    int borderY2 = (((lpbSrc->bmHeight - 2) << 16) - csY) / (int)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    int borderX2 = (((lpbSrc->bmWidth - 2) << 16) - csX) / (int)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    int srcy16 = csY;

#ifdef BLENDALPHA
    DWORD dwAlpha = (DWORD)alpha + 1;
#endif

    for (int y = 0; y < borderY1; ++y)
    {
        int srcx16 = csX;
        for (int x = 0; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (int y = borderY1; y < borderY2; ++y)
    {
        int srcx16 = csX;
        for (int x = 0; x < borderX1; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        DWORD v8 = (srcy16 & 0xFFFF) >> 8;
        PIXEL* pSrcLine = (PIXEL*)((PBYTE)(lpbSrc->bmBits) + lpbSrc->bmWidthBytes * (srcy16 >> 16)) ;
        for (int x = borderX1; x < borderX2; ++x)
        {
            PBYTE pColor0 = (PBYTE)(&pSrcLine[srcx16 >> 16]);
            PBYTE pColor1 = pColor0 + lpbSrc->bmWidthBytes;
#ifndef BLENDALPHA
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x]);
#else
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        for (int x = borderX2; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (int y = borderY2; y < lpbDst->bmHeight; ++y)
    {
        int srcx16 = csX;
        for (int x = 0; x < dstWidth; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

#ifndef BLENDALPHA
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
#else
BOOL BLENDSTRETCH(PBITMAP lpbDst, PBITMAPEX lpbSrc, BYTE alpha, LPCSIZE lpszDst, LPCRECT lprcClipped)
#endif
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy + 1;

    const int csX = -(1 << 15) + ((int)xrIntFloat16 >> 1);
    const int csY = -(1 << 15) + ((int)yrIntFloat16 >> 1);

    int borderY1 = -csY / (int)yrIntFloat16 + 1;
    if (borderY1 >= lpbDst->bmHeight)
        borderY1 = lpbDst->bmHeight;
    int borderX1 = -csX / (int)xrIntFloat16 + 1;
    if (borderX1 >= lpbDst->bmWidth)
        borderX1 = lpbDst->bmWidth;
    int borderY2 = (((lpbSrc->bmHeight - 2) << 16) - csY) / (int)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    int borderX2 = (((lpbSrc->bmWidth - 2) << 16) - csX) / (int)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    DWORD dstByteWidth = lpbDst->bmWidthBytes;
    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    int srcy16 = csY + lprcClipped->top * yrIntFloat16;
    int srcxOriginal16 = csX + lprcClipped->left * xrIntFloat16;

    if (borderX1 > lprcClipped->right) borderX1 = lprcClipped->right;
    if (borderX2 > lprcClipped->right) borderX2 = lprcClipped->right;
    if (borderY1 > lprcClipped->bottom) borderY1 = lprcClipped->bottom;
    if (borderY2 > lprcClipped->bottom) borderY2 = lprcClipped->bottom;
    if (borderX1 < lprcClipped->left) borderX1 = lprcClipped->left;
    if (borderX2 < lprcClipped->left) borderX2 = lprcClipped->left;
    if (borderY1 < lprcClipped->top) borderY1 = lprcClipped->top;
    if (borderY2 < lprcClipped->top) borderY2 = lprcClipped->top;

#ifdef BLENDALPHA
    DWORD dwAlpha = (DWORD)alpha + 1;
#endif

    for (int y = lprcClipped->top; y < borderY1; ++y)
    {
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }
    for (int y = borderY1; y < borderY2; ++y)
    {
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < borderX1; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        DWORD v8 = (srcy16 & 0xFFFF) >> 8;
        PIXEL* pSrcLine = (PIXEL*)((PBYTE)(lpbSrc->bmBits) + lpbSrc->bmWidthBytes * (srcy16 >> 16)) ;
        for (int x = borderX1; x < borderX2; ++x)
        {
            PBYTE pColor0 = (PBYTE)(&pSrcLine[srcx16 >> 16]);
            PBYTE pColor1 = pColor0 + lpbSrc->bmWidthBytes;
#ifndef BLENDALPHA
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x - lprcClipped->left]);
#else
            BLENDPIXELSTRETCH(lpbSrc, pColor0, pColor1, (srcx16 & 0xFFFF) >> 8, v8, &pDstLine[x - lprcClipped->left], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        for (int x = borderX2; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }
    for (int y = borderY2; y < lprcClipped->bottom; ++y)
    {
        int srcx16 = srcxOriginal16;
        for (int x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
#ifndef BLENDALPHA
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
#else
            BLENDBORDERSTRETCH(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left], dwAlpha);
#endif
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }
    return TRUE;
}